﻿// Copyright 2012 Stuart Turner
// Licensed under the Apache License, Version 2.0 [http://www.apache.org/licenses/LICENSE-2.0]

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

using Complete.Data.Support;

namespace Complete.Data.Linq.Expressions
{
	/// <summary>
	/// Extended node types for custom expressions
	/// </summary>
	public enum CompleteExpressionType
	{
		Table = 1000, // make sure these don't overlap with ExpressionType
		Select,
		Column,
		With,
		From,
		Join,
		Where,
		GroupBy,
		OrderBy,
	}

	public static class CompleteExpressionExtensions
	{
		public static bool IsCompleteExpression(this ExpressionType et)
		{
			return ((int)et) >= 1000;
		}

		public static Expression StripQuotes(this Expression e)
		{
			while (e.NodeType == ExpressionType.Quote)
				e = ((UnaryExpression)e).Operand;
			return e;
		}

		public static Expression ReplaceParameter(this Expression exp, ParameterExpression replace, ParameterExpression withThis)
		{
			return ParameterReplacer.ReplaceParameter(exp, replace, withThis);
		}

		private class ParameterReplacer : CompleteExpressionVisitor
		{
			private ParameterExpression _Replace;
			private ParameterExpression _WithThis;
			private ParameterReplacer(ParameterExpression replace, ParameterExpression withThis)
			{
				_Replace = replace;
				_WithThis = withThis;
			}

			public static Expression ReplaceParameter(Expression exp, ParameterExpression replace, ParameterExpression withThis)
			{
				var pr = new ParameterReplacer(replace, withThis);
				return pr.Visit(exp);
			}

			protected override Expression VisitParameter(ParameterExpression p)
			{
				if (p == _Replace)
					return _WithThis;
				return p;
			}
		}
	}

	public abstract class CompleteExpression : Expression
	{
		protected CompleteExpression(CompleteExpressionType eType, Type type)
		{
			this._NodeType = (ExpressionType)eType;
			this._Type = type;
		}

		private Type _Type;
		public override Type Type
		{
			get
			{
				return _Type;
			}
		}

		private ExpressionType _NodeType;
		public override ExpressionType NodeType
		{
			get
			{
				return _NodeType;
			}
		}

		public override string ToString()
		{
			return CompleteExpressionWriter.WriteToString(this);
		}

		public static SelectExpression Select(string alias, LambdaExpression projection, FromExpression from, OrderByClause orderBy, WhereExpression where = null, bool isProjection = true, Expression take = null)
		{
			return new SelectExpression(projection, from, where, alias, isProjection, take, orderBy);
		}

		public static ColumnExpression Column(Expression declaration, string name, string alias = null, int ordinal = 0)
		{
			return new ColumnExpression(alias, name, ordinal, declaration);
		}

		public static FromExpression From(SourceExpression from)
		{
			return new FromExpression(from);
		}

		public static TableExpression Table(Type type, string alias, string name, LambdaExpression projection)
		{
			return new TableExpression(type, alias, name,  projection);
		}

		public static JoinExpression Join(SourceExpression left, SourceExpression right, JoinType joinType, Expression comparison = null)
		{
			return new JoinExpression(left, right, joinType, comparison);
		}

		public static WhereExpression Where(Expression predicate, WhereExpression previousWhere = null)
		{
			return new WhereExpression(predicate, previousWhere);
		}
	}
}